/*
 * Quercia Luciano
 * Algoritmi e Strutture Dati
 * a.a. 2009-10
 */


#ifndef _LISTA_H_
#define _LISTA_H_

#define NDEBUG
#include <cassert>

#include <iostream>
using namespace std;


#include "nodoL.h"

template <class T>
class Lista {
public:
	Lista();
	~Lista();

	typedef NodoL<T> * posizione;

	//Operatori
	bool listaVuota() const ;
	bool fineLista(posizione) const ;
	posizione primoLista() const ;
	posizione ultimoLista() const ;
	posizione succLista(posizione) const ;
	posizione precLista(posizione) const ;
	T leggiLista(posizione) const  throw (char const *);
	void scriviLista(T, posizione) ;
	void insLista(T, posizione) ;
	void cancLista(posizione)  throw (char const *) ;

	//overloading operatori
	Lista operator+(Lista);


	//operatori personalizzati
	void print(void);
	void append( T );
	posizione direct( int );
	int sizeLista();

private:
	int nNodi;
	posizione top;
};



/*****************************
 *        DEFINIZIONI        *
 *     dei metodi di Lista   *
 *****************************/

template <class T>
Lista<T>::Lista() {
	nNodi = 0 ;
	posizione sentinella = new NodoL<T>;
	top = sentinella ;
	sentinella->setPrev(sentinella);
	sentinella->setSucc(sentinella);
}

template <class T>
Lista<T>::~Lista() {}


template <class T>
bool Lista<T>::listaVuota() const {
	return ( nNodi == 0 );
}

template <class T>
bool Lista<T>::fineLista(posizione cella) const {
	return ( cella == top ) ;
}


template <class T>
NodoL<T> * Lista<T>::primoLista() const {
	return top->getSucc() ;
}


template <class T>
typename Lista<T>::posizione Lista<T>::ultimoLista() const {
	return top->getPrev() ;
}


template <class T>
typename Lista<T>::posizione Lista<T>::succLista(posizione cella) const {
	posizione successivo;
	successivo = cella->getSucc();
	return successivo;
}


template <class T>
typename Lista<T>::posizione Lista<T>::precLista(posizione cella) const {
	posizione precedente;
	precedente = cella->getPrev();
	return precedente;
}


template <class T>
T Lista<T>::leggiLista(posizione cella) const throw (char const *) {
	if ( !listaVuota() )
		return cella->getValue() ;
	else
		throw "Impossibile leggere da una Lista vuota." ;
}

template <class T>
void Lista<T>::scriviLista(T elem, posizione cella) {
	cella->setValue(elem) ;
}


template <class T>
void Lista<T>::insLista(T elem, posizione cella) {
	posizione temp= new NodoL<T>;

	temp->setValue(elem);
	temp->setPrev( cella->getPrev() );
	temp->setSucc( cella );

	cella->getPrev()->setSucc( temp );
	cella->setPrev( temp );
	nNodi++;
}


template <class T>
void Lista<T>::cancLista(posizione cella) throw (char const *) {
	if ( !listaVuota() ) //se non è vuota
		if  ( cella != top ) { //se non è la sentinella
			posizione precedente;
			posizione successivo;
			precedente = cella->getPrev();
			successivo = cella->getSucc();

			precedente->setSucc( successivo );
			successivo->setPrev( precedente );

			delete cella;

			nNodi--;
		}
		else
			throw "Impossibile effettuare l'operazione." ;
	else
		throw "Impossibile cancellare da una Lista vuota." ;
}


template <class T>
void Lista<T>::print(void) {
	cout << "[ " ;

	if ( ! listaVuota() ) {
		NodoL<T> * p;
		p = primoLista();
		while ( ! fineLista(p) ) {
			cout << ( leggiLista(p) ) << " ";
			p = succLista(p);
		}
	}
	else
		cout << "\tEmpty List\n" ;

	cout << "]\n" ;
}

template <class T>
Lista<T> Lista<T>::operator+(Lista<T> list) {
	Lista<T> temp;

	posizione p;

	p = this->primoLista();
	while ( ! (this->fineLista(p)) ) {
		temp.append( this->read(p) );
		this->succLista(p);
	}

	p = list.primoLista();
	while ( ! (list.fineLista(p)) )	{
		temp.append( list.read(p) );
		list->succLista(p);
	}

	return temp;
}

template <class T>
typename Lista<T>::posizione Lista<T>::direct(int k) {
	posizione p;
	int i;

	if ( k < 0 )
		k = -k; //soluzione temporanea

	if ( k == 0 )
		k = 1; //indice 0 inteso come primo posto

	if ( k > 0 ) {
		if ( k > ( sizeLista() +1)  )
			k = ( k % (sizeLista() +1 ));


		p = primoLista();
		for (i=1; i<k; i++)
			p = succLista(p);
	}
	return p;
}

template <class T>
void Lista<T>::append( T elem ) {
	insert( elem , direct(nNodi+1) );
}


template <class T>
int Lista<T>::sizeLista() {
	return nNodi;
}


#endif
